// --------------------------------------------------------------------------------------------------------------------
// <copyright file="FileRequest.cs" company="XamlNinja">
//   2011 Richard Griffin and Ollie Riches
// </copyright>
// <summary>
//   FileRequest.cs
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace WP7Contrib.Services.DownloadManager
{
    using System;
    using System.ComponentModel;
    using System.Reactive.Linq;
    using Common;


    /// <summary>
    /// Defines a file download request, all properties are read-only. This class is exposed via the IDownloadFiles interface.
    /// </summary>
    public sealed class FileRequest : BaseModel, IEquatable<FileRequest>
    {
        private const int KiloByte = 1024;

        private Guid identifier;
        private DateTime createdOn;
        private string url;
        private long contentLength;
        private string filePath;
        private FileRequestState state;
        private long fileLength;
        private bool resumable;
        private int blockSize;
        private TimeSpan duration;
        private Exception exception;

        public FileRequest(IFileRequestPropertyChanged request)
        {
            this.HandleRequestChanged(request);

            Observable.FromEventPattern<PropertyChangedEventArgs>(request, "PropertyChanged")
                .ObserveOnDispatcher()
                .Subscribe(args => this.HandleRequestChanged(request));
        }

        private void HandleRequestChanged(IFileRequestPropertyChanged request)
        {
            this.Identifier = request.Identifier;
            this.CreatedOn = request.CreatedOn;
            this.Url = request.UrlAsString;
            this.FilePath = request.FilePath;
            this.State = request.State;
            this.Duration = request.Duration;
            this.ContentLength = request.ContentLength;
            this.FileLength = request.FileLength;
            this.Resumable = request.Resumable;
            this.BlockSize = request.BlockSize;
            this.Exception = request.Exception;
        }

        /// <summary>
        /// The unique identifier for the file download request.
        /// </summary>
        public Guid Identifier
        {
            get
            {
                return this.identifier;
            }
            private set
            {
                SetPropertyAndNotify(ref this.identifier, value, () => Identifier);
            }
        }

        /// <summary>
        /// The date the request was created.
        /// </summary>
        public DateTime CreatedOn
        {
            get
            {
                return this.createdOn;
            }
            private set
            {
                SetPropertyAndNotify(ref this.createdOn, value, () => CreatedOn);
            }
        }

        /// <summary>
        /// The url of the file being downloaded.
        /// </summary>
        public string Url
        {
            get
            {
                return this.url;
            }
            private set
            {
                SetPropertyAndNotify(ref this.url, value, () => Url);
            }
        }

        /// <summary>
        /// The content length of the file at the Url.
        /// </summary>
        public long ContentLength
        {
            get
            {
                return this.contentLength;
            }
            private set
            {
                SetPropertyAndNotify(ref this.contentLength, value, () => ContentLength);
                this.RaisePropertyChanged("PercentageCompleted");
                this.RaisePropertyChanged("PercentageRemaining");
            }
        }

        /// <summary>
        /// The local file path, this is where the file when downloaded will be copied too.
        /// </summary>
        public string FilePath
        {
            get
            {
                return this.filePath;
            }
            private set
            {
                SetPropertyAndNotify(ref this.filePath, value, () => FilePath);
            }
        }

        /// <summary>
        /// The current state of this request.
        /// </summary>
        public FileRequestState State
        {
            get
            {
                return this.state;
            }
            private set
            {
                SetPropertyAndNotify(ref this.state, value, () => State);
            }
        }

        /// <summary>
        /// The accumlated duration.
        /// </summary>
        public TimeSpan Duration
        {
            get
            {
                return this.duration;
            }
            private set
            {
                SetPropertyAndNotify(ref this.duration, value, () => Duration);
            }
        }

        /// <summary>
        /// The local file length.
        /// </summary>
        public long FileLength
        {
            get
            {
                return this.fileLength;
            }
            private set
            {
                SetPropertyAndNotify(ref this.fileLength, value, () => FileLength);
                this.RaisePropertyChanged("PercentageCompleted");
                this.RaisePropertyChanged("PercentageRemaining");
            }
        }

        /// <summary>
        /// Defines if this file request is a resumable download.
        /// </summary>
        public bool Resumable
        {
            get
            {
                return this.resumable;
            }
            private set
            {
                SetPropertyAndNotify(ref this.resumable, value, () => Resumable);
            }
        }

        /// <summary>
        /// The block size used for resumable downloads.
        /// </summary>
        public int BlockSize
        {
            get
            {
                return this.blockSize;
            }
            private set
            {
                SetPropertyAndNotify(ref this.blockSize, value, () => BlockSize);
            }
        }

        /// <summary>
        /// The exception for the resumable downloads.
        /// </summary>
        public Exception Exception
        {
            get
            {
                return this.exception;
            }
            private set
            {
                SetPropertyAndNotify(ref this.exception, value, () => Exception);
            }
        }

        /// <summary>
        /// The percentage of download completed.
        /// </summary>
        public double PercentageCompleted
        {
            get
            {
                if (this.ContentLength == 0 || this.FileLength == 0)
                {
                    return 0;
                }

                var percentage = ((double)this.FileLength / (double)this.ContentLength) * 100;
                return Math.Round(percentage, 3);
            }
        }

        /// <summary>
        /// The percentage of download remaining.
        /// </summary>
        public double PercentageRemaining
        {
            get { return 100 - this.PercentageCompleted; }
        }

        public double DownloadRate
        {
            get
            {
                return ( (double) this.FileLength / KiloByte ) / this.Duration.TotalSeconds;
            }
        }

        public static bool operator ==(FileRequest fr1, FileRequest fr2)
        {
            return Equals(fr1, fr2);
        }

        public static bool operator !=(FileRequest fr1, FileRequest fr2)
        {
            return !Equals(fr1, fr2);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }

            return obj is FileRequest && this.Equals((FileRequest)obj);
        }

        public override int GetHashCode()
        {
            return this.CombineHashCodes(this.Identifier, this.Url, this.FilePath, this.State, this.Duration, this.ContentLength, this.FileLength, this.Resumable, this.BlockSize);
        }

        public bool Equals(FileRequest request)
        {
            if (ReferenceEquals(null, request))
            {
                return false;
            }

            if (this.Identifier != request.Identifier)
            {
                return false;
            }

            if (this.Url != request.Url)
            {
                return false;
            }

            if (this.FilePath != request.FilePath)
            {
                return false;
            }

            if (this.State != request.State)
            {
                return false;
            }

            if (this.Duration != request.Duration)
            {
                return false;
            }

            if (this.ContentLength != request.ContentLength)
            {
                return false;
            }

            if (this.FileLength != request.FileLength)
            {
                return false;
            }

            if (this.Resumable != request.Resumable)
            {
                return false;
            }

            if (this.BlockSize != request.BlockSize)
            {
                return false;
            }

            return true;
        }
    }
}